package top.littlejiang.onlinexam.common.configuration;

import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import redis.clients.jedis.exceptions.JedisException;
import top.littlejiang.onlinexam.common.base.BusinessException;
import top.littlejiang.onlinexam.common.base.LoginException;
import top.littlejiang.onlinexam.common.utils.Result;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.io.IOException;

/**
 * @author chen
 * @Description
 * @since 2021/1/2 18:22
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * shiro异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(ShiroException.class)
    @ResponseBody
    public Result handle401(ShiroException e) {
        log.error(e.getMessage(), e);
        return Result.build(Result.CODE_UNAUTHORIZED, "授权错误！", null);
    }

    /**
     * 未授权异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(UnauthenticatedException.class)
    @ResponseBody
    public Result handle401(UnauthenticatedException e) {
        log.error(e.getMessage());
        return Result.build(Result.CODE_UNAUTHORIZED, Result.MSG_UNAUTHORIZED, null);
    }

    /**
     * 没有权限 （@RequiresPermissions）
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public Result handle401(UnauthorizedException e) {
        log.error(e.getMessage());
        return Result.build(Result.CODE_UNAUTHORIZED, Result.MSG_UNAUTHORIZED, null);
    }


    /**
     * 业务异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public Result handler(BusinessException e) {
        return Result.build(Result.CODE_FAIL, e.getMessage());
    }

    /**
     * 登录异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = LoginException.class)
    @ResponseBody
    public Result handler(LoginException e) {
        return Result.build(Result.CODE_LOGIN, e.getMessage());
    }

    /**
     * 运行时异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public Result handle(RuntimeException e) {
        log.error(e.toString(), e);
        return Result.build(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统错误，请联系管理员！", null);
    }

    /**
     * redis异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JedisException.class)
    @ResponseBody
    public Result handle(JedisException e) {
        log.error("redis error {}", e);
        return Result.build(HttpStatus.INTERNAL_SERVER_ERROR.value(), "系统错误", null);
    }

    /**
     * token过期
     *
     * @param e
     * @return
     * @throws IOException
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = TokenExpiredException.class)
    @ResponseBody
    public Result handler(TokenExpiredException e) throws IOException {
        return Result.build(Result.CODE_LOGIN, "登录状态已过期，请重新登录", null);
    }


    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Result BindExceptionHandler(BindException e) {
        return Result.build(Result.CODE_FAIL, e.getAllErrors().get(0).getDefaultMessage());
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public Result ConstraintViolationExceptionHandler(ConstraintViolationException e) {
        return Result.build(Result.CODE_FAIL, "参数错误");
    }

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        return Result.build(Result.CODE_FAIL, e.getMessage());
    }

    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    public Result ValidationExceptionHandler(ValidationException e) {
        return Result.build(Result.CODE_FAIL, "参数检验错误！");
    }
}
